/*
Eteria IRC Client, an RFC 1459 compliant client program written in Java.
Copyright (C) 2000-2001  Javier Kohen <jkohen at tough.com>

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

package ar.com.jkohen.irc;

import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Observable;

public class Channel extends Observable
{
    public static final int TOPIC_MASK = 0x01;
	public static final int MODERATED_MASK = 0x02;
	public static final int SECRET_MASK = 0x04;
	public static final int INVITATION_MASK = 0x08;
	public static final int REGISTERED_MASK = 0x10;
	public static final int NONICK_MASK = 0x20;

    private String tag;
    private String topic;
	private int limit = -1;
	private String key = "";
    private int modes_mask;

    private Hashtable users;

    public Channel(String tag)
    {
		this.tag = tag;
		this.users = new Hashtable();
    }

    public void refresh()
    {
		setChanged();
		notifyObservers("mode");
    }
	
    public String getTag()
    {
		return tag;
    }

    /* Method setTag makes no sense as a channel's tag can't be changed. */

    public String getTopic()
    {
		return topic;
    }

    public void setTopic(String topic)
    {
		this.topic = topic;
		setChanged();
		notifyObservers("topic");
    }

    /*
     * User related methods.
     */

    public void add(String nick)
    {
		int mask = Modes.symbolicToMask(nick.charAt(0));
		nick = Modes.canonicalizeNick(nick);
		User user = new User(nick);
		user.setModes(mask);
		synchronized (users)
		{
	    	users.put(nick, user);
		}

		setChanged();
		notifyObservers("add ".concat(nick));
    }

    public void remove(String nick)
    {
		synchronized (users)
		{
		    users.remove(nick);
		}

		setChanged();
		notifyObservers("remove ".concat(nick));
    }

    public void rename(String nick, String new_nick)
    {
		synchronized (users)
		{
	    	User user = (User) users.remove(nick);
	    	user.setTag(new_nick);
	    	users.put(new_nick, user);
		}

		setChanged();
		notifyObservers("rename ".concat(nick).concat(" ").concat(new_nick));
    }
	
	public void clean()
	{
		synchronized (users)
		{
			users.clear();
		}
		setChanged();
	}

    public User get(String nick)
    {
  		return (User) users.get(nick);
    }

    public boolean contains(String nick)
    {
		return users.containsKey(nick);
    }

	public int number()
	{
		return(users.size());
	}
	
    public User [] elements()
    {
		User [] a;
		Enumeration e;

		synchronized (users)
		{
			a = new User [users.size()];
		    e = users.elements();
		}

		for (int i = 0; e.hasMoreElements(); i++)
		    a[i] = (User) e.nextElement();

		return a;
    }


    /*
     * Mode related methods.
     */

    public int getLimit()
    {
		return(limit);
    }

    public void setLimit(String l)
    {
		try
		{
			this.limit = Integer.parseInt(l);
		} catch (NumberFormatException e) { this.limit = -1; }
    }
	
    public String getKey()
    {
		return(key);
    }

    public void setKey(boolean sign, String k)
    {
		if (sign)
			this.key = k;
		else
			this.key = "";
    }
	
    /**
     * Set channel modes.
     */
     
    public void setModes(String modes, String [] params)
    {
		char [] mode_ary = modes.toCharArray();
		boolean sign = false;

    	int j = 0;
    	for (int i = 0; i < mode_ary.length; i++)
    	{
    	    switch (mode_ary[i])
    	    {
    	    case '+':
    			sign = true;
    			break;
    	    case '-':
	    		sign = false;
    			break;
    			
    		/* Channel modes which handle additional parameters */

    	    case 'v':
				if (j < params.length)
    				setUserModes(params[j++], sign, User.VOICE_MASK);
    			break;
    	    case 'h':
				if (j < params.length)
	    			setUserModes(params[j++], sign, User.HALFOP_MASK);
    			break;
    	    case 'o':
				if (j < params.length)
	    			setUserModes(params[j++], sign, User.OP_MASK);
    			break;
			case 'a':
				if (j < params.length)
	    			setUserModes(params[j++], sign, User.ADMIN_MASK);
    			break;
			case 'q':
				if (j < params.length)
	    			setUserModes(params[j++], sign, User.OWNER_MASK);
    			break;

    	    case 'k':
				if (j < params.length)
					setKey(sign, params[j++]);
				break;
    	    case 'l':
				if (j < params.length)
					setLimit(sign ? params[j++] : "");
				break;
		
    			
    		/* Channel modes without parameters */
    			
    	    case 't':
    			setChannelModes(sign, Channel.TOPIC_MASK);
	    		break;
    	    case 'm':
    			setChannelModes(sign, Channel.MODERATED_MASK);
	    		break;
    	    case 's':
    			setChannelModes(sign, Channel.SECRET_MASK);
	    		break;
    	    case 'i':
    			setChannelModes(sign, Channel.INVITATION_MASK);
	    		break;
			
			/* Non RFC1459 channel modes */
			
    	    case 'r':
    			setChannelModes(sign, Channel.REGISTERED_MASK);
	    		break;
    	    case 'N':
    			setChannelModes(sign, Channel.NONICK_MASK);
	    		break;
			
    		/* These modes aren't handled, but are known to carry a parameter which must be skipped. */
    		
    	    case 'b':
    	    case 'e':
    	    case 'I':
    			j++;
    			break;
    	    }
		}

		setChanged();
		notifyObservers("mode");
    }

    private void setUserModes(String nick, boolean sign, int mask)
    {
		User user = get(nick);
		if(user == null)
			return;
			
		int cur_mask = user.getModes();

		if (sign)
	    	cur_mask |= mask;
		else
	    	cur_mask &= ~mask;


		user.setModes(cur_mask);
    }

    private void setChannelModes(boolean sign, int mask)
    {
		if (sign)
	    	modes_mask |= mask;
		else
	    	modes_mask &= ~mask;
    }

    public boolean isTopicSettable()
    {
		return ((modes_mask & TOPIC_MASK) == 0);
    }
	
    public boolean isModerated()
    {
		return ((modes_mask & MODERATED_MASK) != 0);
    }

    public boolean isInvitOnly()
    {
		return ((modes_mask & INVITATION_MASK) != 0);
    }

    public boolean isSecret()
    {
		return ((modes_mask & SECRET_MASK) != 0);
    }

    public boolean isRegistered()
    {
		return ((modes_mask & REGISTERED_MASK) != 0);
    }
	
    public boolean canNick()
    {
		return ((modes_mask & NONICK_MASK) == 0);
    }
		
    public static boolean isChannel(String s)
	{
		if (s != null && !s.equals(""))
		{
			char ch = s.charAt(0);
			return (ch == '&' || ch == '#' || ch == '+' || ch == '!');
		}
		return (false);
    }
}
